#include "roommanager.h"

std::auto_ptr<RoomManager> RoomManager::_mgr;

RoomManager::~RoomManager()
{
    std::map<std::string,Room *>::iterator it;
    for(it = _room_map.begin(); it != _room_map.end(); it++)
        delete it->second;
}

std::auto_ptr<RoomManager>& RoomManager::getInstance()
{
    if(_mgr.get() == 0){
        _mgr.reset(new RoomManager());
    }

    return _mgr;
}

Room* RoomManager::getRoomByName(const std::string& room_name)
{
    std::map<std::string,Room *>::iterator iter = _room_map.find(room_name);
    if(iter != _room_map.end())
        return iter->second;
    return 0;
}

Room *RoomManager::getRoomByClient(const Client& client)
{
    Room *room = 0;
    if(!client.empty()){
        std::map<ACE_HANDLE,std::string>::iterator iter;
        iter = _client_map.find(client.getSOCK().get_handle());
        if(iter != _client_map.end())
            return getRoomByName(iter->second);
    }

    return room;
}

qint32 RoomManager::createRoom(ACE_SOCK_Stream &peer,const std::string &name,const std::string &password)
{
    if(!name.length())
        return E_ROOM_NAME_EMPTY;

    if(_room_map.count(name))   //room exist
        return E_ROOM_EXIST;

    if(_client_map.count(peer.get_handle()))
	return E_ROOM_ALREAY_IN_ROOM;

    Client creator(peer);

    Room *room = 0;
    try{
        room = new Room(name,password);
    }
    catch(...) {
        return E_MEMORY;
    }

    room->setOwner(creator);

    _room_map.insert(std::pair<std::string,Room *>(name,room));
    _client_map.insert(std::pair<ACE_HANDLE,std::string>(peer.get_handle(),name));

    return E_SUCCESS;
}

qint32 RoomManager::addClient2Room(ACE_SOCK_Stream &peer,const std::string &room_name,const std::string &password)
{
    if(!room_name.length())
        return E_ROOM_NAME_EMPTY;

    Room *room = getRoomByName(room_name);
    if(!room)
        return E_ROOM_NOT_EXIST;

    Client member(peer);
    if(!room->addClient(member,password))
        return E_ROOM_NOT_EXIST_OR_WRONG_PASSWD;

    _client_map.insert(std::pair<ACE_HANDLE,std::string>(peer.get_handle(),room_name));
    return E_SUCCESS;
}

bool RoomManager::removeClientFromRoom(ACE_SOCK_Stream &peer,const std::string &room_name)
{
    if(room_name.length()){
        Room *room = getRoomByName(room_name);
        if(room){
	    Client client(peer);
	    room->removeClient(client);
	    _client_map.erase(peer.get_handle());

            if(room->size() == 0)
                _room_map.erase(room_name);

            return true;
        }
    }

    return false;
}

bool RoomManager::removeClient(ACE_HANDLE handle)
{
    std::map<ACE_HANDLE,std::string>::iterator it = _client_map.find(handle);
    if(it == _client_map.end())
	return false;

    ACE_SOCK_Stream peer(handle);
    return removeClientFromRoom(peer,it->second);
}

void RoomManager::dump(std::map<std::string,std::pair<quint8,bool> > &info)
{
    if(_room_map.size()){
	std::map<std::string,Room *>::iterator iter;
	for(iter = _room_map.begin();iter != _room_map.end();iter++){
	    std::pair<quint8,bool> &attr = info[iter->first];
	    Room *room = iter->second;

	    attr.first = room->size();
	    attr.second = room->isReady();
	}
    }
}

bool RoomManager::broadcast(const std::string &room,std::auto_ptr<SimpleProtoSrv> &msg)
{
    if(msg.get() && _client_map.size())
    {
        std::map<ACE_HANDLE,std::string>::iterator iter;
        for(iter = _client_map.begin(); iter != _client_map.end(); iter++)
        {
            if(iter->second == room)
            {
                ACE_SOCK_Stream peer(iter->first);
                if(msg->execute(peer) != E_SUCCESS){
                    ACE_INET_Addr addr;
                    peer.get_remote_addr(addr);
                    ACE_DEBUG((LM_DEBUG,"Broadcast Fail(%s:%d,%d:%s,reason:%s)\n"
                        ,addr.get_host_addr(),addr.get_port_number(),msg->getProtocolType(),typeid(*msg).name(),strerror(errno)));
                }
            }
        }
    }

    return true;
}
